package services

import (
	"bytes"
	"chinaport-proxy/auth"
	"context"
	"encoding/json"
	"fmt"
	"log/slog"
	"net/http"
	"net/url"
	"strconv"
	"time"
)

// WebhookService handles webhook notifications
type WebhookService struct {
	failureURL string
	auditURL   string
	appSecret  string
	client     *http.Client
}

// NewWebhookService creates a new webhook service
func NewWebhookService(failureURL, auditURL, appSecret string) *WebhookService {
	if failureURL == "" && auditURL == "" {
		return nil
	}

	return &WebhookService{
		failureURL: failureURL,
		auditURL:   auditURL,
		appSecret:  appSecret,
		client: &http.Client{
			Timeout: 10 * time.Second,
		},
	}
}

// WebhookPayload represents the webhook notification payload
type WebhookPayload struct {
	Event     string                 `json:"event"`
	Timestamp int64                  `json:"timestamp"`
	Error     string                 `json:"error,omitempty"`
	Details   map[string]interface{} `json:"details,omitempty"`
}

// AuditPayload represents the audit webhook payload
type AuditPayload struct {
	Event     string      `json:"event"`
	Timestamp int64       `json:"timestamp"`
	RequestID string      `json:"request_id"`
	Method    string      `json:"method"`
	Request   interface{} `json:"request"`
	Response  interface{} `json:"response,omitempty"`
	Success   bool        `json:"success"`
	Error     string      `json:"error,omitempty"`
	Duration  int64       `json:"duration_ms,omitempty"`
}

// sendWebhook is a helper to send webhook notifications
func (s *WebhookService) sendWebhook(ctx context.Context, webhookURL string, payload interface{}) error {
	if webhookURL == "" {
		return nil
	}

	// Marshal payload
	bodyBytes, err := json.Marshal(payload)
	if err != nil {
		return fmt.Errorf("failed to marshal webhook payload: %w", err)
	}

	// Parse URL to get the path
	parsedURL, err := url.Parse(webhookURL)
	if err != nil {
		return fmt.Errorf("failed to parse webhook URL: %w", err)
	}

	// Generate timestamp and signature (use path only, not full URL)
	timestamp := auth.GetCurrentTimestamp()
	signature := auth.GenerateSignature("POST", parsedURL.Path, timestamp, string(bodyBytes), s.appSecret)

	// Create request
	req, err := http.NewRequestWithContext(ctx, "POST", webhookURL, bytes.NewBuffer(bodyBytes))
	if err != nil {
		return fmt.Errorf("failed to create webhook request: %w", err)
	}

	// Set headers
	req.Header.Set("X-Timestamp", strconv.FormatInt(timestamp, 10))
	req.Header.Set("X-Signature", signature)
	req.Header.Set("Content-Type", "application/json; charset=utf-8")

	// Send request
	resp, err := s.client.Do(req)
	if err != nil {
		slog.Error("Failed to send webhook", "url", webhookURL, "error", err)
		return fmt.Errorf("failed to send webhook: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode >= 300 {
		slog.Warn("Webhook returned non-success status",
			"status", resp.StatusCode,
			"url", webhookURL)
		return fmt.Errorf("webhook returned status %d", resp.StatusCode)
	}

	return nil
}

// NotifyMalfunction sends a malfunction notification to the failure webhook
func (s *WebhookService) NotifyMalfunction(ctx context.Context, errorMsg string, details map[string]interface{}) error {
	if s == nil || s.failureURL == "" {
		return nil
	}

	// Create payload
	payload := WebhookPayload{
		Event:     "ukey_malfunction",
		Timestamp: auth.GetCurrentTimestamp(),
		Error:     errorMsg,
		Details:   details,
	}

	slog.Info("Sending failure webhook notification",
		"url", s.failureURL,
		"event", "ukey_malfunction",
		"error", errorMsg)

	err := s.sendWebhook(ctx, s.failureURL, payload)
	if err == nil {
		slog.Info("Failure webhook notification sent successfully", "url", s.failureURL)
	}
	return err
}

// NotifyConnectionFailure notifies about WebSocket connection failures
func (s *WebhookService) NotifyConnectionFailure(ctx context.Context, err error) {
	if s == nil {
		return
	}

	details := map[string]interface{}{
		"type": "connection_failure",
	}

	if err := s.NotifyMalfunction(ctx, fmt.Sprintf("WebSocket connection failed: %v", err), details); err != nil {
		slog.Error("Failed to notify connection failure", "error", err)
	}
}

// NotifyOperationFailure notifies about UKey operation failures
func (s *WebhookService) NotifyOperationFailure(ctx context.Context, method string, errors []string) {
	if s == nil {
		return
	}

	details := map[string]interface{}{
		"type":   "operation_failure",
		"method": method,
		"errors": errors,
	}

	errorMsg := fmt.Sprintf("UKey operation failed: %s", method)
	if len(errors) > 0 {
		errorMsg = fmt.Sprintf("%s - %v", errorMsg, errors)
	}

	if err := s.NotifyMalfunction(ctx, errorMsg, details); err != nil {
		slog.Error("Failed to notify operation failure", "error", err)
	}
}

// NotifyHealthCheckFailure notifies about health check failures
func (s *WebhookService) NotifyHealthCheckFailure(ctx context.Context, err error) {
	if s == nil {
		return
	}

	details := map[string]interface{}{
		"type": "health_check_failure",
	}

	if err := s.NotifyMalfunction(ctx, fmt.Sprintf("Health check failed: %v", err), details); err != nil {
		slog.Error("Failed to notify health check failure", "error", err)
	}
}

// SendAudit sends an audit webhook for request/response tracking
func (s *WebhookService) SendAudit(ctx context.Context, requestID, method string, request, response interface{}, success bool, errorMsg string, durationMs int64) {
	if s == nil || s.auditURL == "" {
		return
	}

	payload := AuditPayload{
		Event:     "api_audit",
		Timestamp: auth.GetCurrentTimestamp(),
		RequestID: requestID,
		Method:    method,
		Request:   request,
		Response:  response,
		Success:   success,
		Error:     errorMsg,
		Duration:  durationMs,
	}

	slog.Debug("Sending audit webhook",
		"url", s.auditURL,
		"request_id", requestID,
		"method", method,
		"success", success)

	// Send asynchronously to avoid blocking the main request
	go func() {
		if err := s.sendWebhook(context.Background(), s.auditURL, payload); err != nil {
			slog.Error("Failed to send audit webhook", "error", err)
		}
	}()
}
